Notebook Purpose

This notebook serves to summarize the entire visualization process going from the travel time matrix to the visualizations. That involves the following sections:

  1. Importing and Linking Geospatial Data
  2. Interactive Visualization
  3. Map HTML Exports

0) Useful Libraries

# import custom scoring, cleaning, and visualization functions
source('../0 - custom_functions/functions.R')

# wrangling/convenience
library(tidyverse)
library(glue)
library(stringr)
library(sf)
library(data.table)
library(bit64)

# visualization
library(leaflet)
library(mapview); mapviewOptions(platform = 'leafgl')

# For pretty knitting
library(lemon)
knit_print.data.frame <- lemon_print
knit_print.tbl <- lemon_print
knit_print.summary <- lemon_print

shapepath <- "../../data/1_raw/shape_files"
comppath <- "../../data/3_computed/"
mappath <- "../../data/html_maps"    # output directory

1) Importing Data

Import the Accessibility Measures

scores_frame <- read.csv(paste0(comppath, '/accessibility_measures/scores_frame.csv'))
isochr_frame <- read.csv(paste0(comppath, '/accessibility_measures/isochrone_frame.csv'))

# convert factor columns to factor
scores_frame[, c(1,2,4,5)] <- lapply(scores_frame[, c(1,2,4,5)], as.factor)
isochr_frame[, c(1,2,3)] <- lapply(isochr_frame[, c(1,2,3)], as.factor)

head(scores_frame)
head(isochr_frame)
NA

Import the dissemination block shape file

Note: The shape file being imported was preproccessed from a national census shape file and does not need to be filtered/cleaned any further. GitBash scripting (via jq library) was used to extract 36 megabytes of Vancouver polygon data from 1.6 gigabytes of Canada wide geoJson polygon data, which R nor Python could efficiently handle.


vancouver_shape <- st_read(paste0(shapepath, '/DB_Van_CMA.shp'), stringsAsFactors = FALSE)
Reading layer `DB_Van_CMA' from data source `C:\Users\Luka\Documents\GitHub\Capstone-Project\data\1_raw\shape_files\DB_Van_CMA.shp' using driver `ESRI Shapefile'
Simple feature collection with 15197 features and 27 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 4001643 ymin: 1957237 xmax: 4068119 ymax: 2032663
Projected CRS: PCS_Lambert_Conformal_Conic
# id to factor
vancouver_shape$DBUID <- as.factor(vancouver_shape$DBUID)
head(vancouver_shape)
Simple feature collection with 6 features and 27 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 4019407 ymin: 2002663 xmax: 4033248 ymax: 2007975
Projected CRS: PCS_Lambert_Conformal_Conic
        DBUID DBRPLAMX DBRPLAMY PRUID                                  PRNAME CDUID            CDNAME CDTYPE  CCSUID
1 59150244005  4030984  2005717    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915020
2 59150244008  4032066  2007480    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915020
3 59150372006  4019917  2002902    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915022
4 59150372007  4019852  2002834    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915022
5 59150373001  4019547  2002835    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915022
6 59150373002  4019483  2002723    59 British Columbia / Colombie-Britannique  5915 Greater Vancouver     RD 5915022
              CCSNAME  CSDUID         CSDNAME CSDTYPE ERUID                                                ERNAME FEDUID
1 Greater Vancouver A 5915046 North Vancouver      DM  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59002
2 Greater Vancouver A 5915046 North Vancouver      DM  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59002
3           Vancouver 5915022       Vancouver      CY  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59038
4           Vancouver 5915022       Vancouver      CY  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59038
5           Vancouver 5915022       Vancouver      CY  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59038
6           Vancouver 5915022       Vancouver      CY  5920 Lower Mainland--Southwest / Lower Mainland--Sud-ouest  59038
                                         FEDNAME SACCODE SACTYPE CMAUID CMAPUID   CMANAME CMATYPE      CTUID  CTNAME
1 Burnaby North--Seymour / Burnaby-Nord--Seymour     933       1    933   59933 Vancouver       B 9330110.03 0110.03
2 Burnaby North--Seymour / Burnaby-Nord--Seymour     933       1    933   59933 Vancouver       B 9330110.03 0110.03
3                             Vancouver Kingsway     933       1    933   59933 Vancouver       B 9330032.00 0032.00
4                             Vancouver Kingsway     933       1    933   59933 Vancouver       B 9330032.00 0032.00
5                             Vancouver Kingsway     933       1    933   59933 Vancouver       B 9330032.00 0032.00
6                             Vancouver Kingsway     933       1    933   59933 Vancouver       B 9330032.00 0032.00
    ADAUID    DAUID                       geometry
1 59150014 59150244 MULTIPOLYGON (((4031013 200...
2 59150014 59150244 MULTIPOLYGON (((4031720 200...
3 59150082 59150372 MULTIPOLYGON (((4019999 200...
4 59150082 59150372 MULTIPOLYGON (((4019845 200...
5 59150082 59150373 MULTIPOLYGON (((4019658 200...
6 59150082 59150373 MULTIPOLYGON (((4019485 200...

Import bus stop network data

# Read Vancouver Station txt file
rawlocs <- read.csv("../../data/1_raw/transit_and_osm_data/stops.txt",
                    head = TRUE, sep=",")

station_locs <- rawlocs[c("stop_id","stop_name","stop_lat","stop_lon")]

colnames(station_locs)[3] <- "latitude"
colnames(station_locs)[4] <- "longitude"

# Convert the columns imported as a factor to characters
station_locs$stop_id <- as.character(station_locs$stop_id)
station_locs$stop_name <- as.character(station_locs$stop_name)

# check for NA rows
station_locs[is.na(as.numeric(station_locs$stop_id)), ]

# check how many stops there are
uniqueN(station_locs$stop_id)
[1] 8841
head(station_locs)
NA

Joining the GeoSpatial Data


# merges shape and visualization data, then transforms them for visualizations
# crs = target coordinate reference system
mapping_data_prepper <- function(shape_data, visualization_data,
                                 by = NULL, crs = 4326) {
  if (is.null(by)) {
    message("Must provide key or columns to join on. For example, on = c('ID' = 'id'))")
    return()
  }
  
  # join both datasets on the shapes so all polygons are included
  shape_viz_frame <- left_join(shape_data, visualization_data, by = by)
  
  # convert to simple feature object (sf) then transform coords
  return(st_transform(st_as_sf(shape_viz_frame), crs = crs))
    
}

scores_viz_frame <- mapping_data_prepper(vancouver_shape, scores_frame,
                                         by = c('DBUID' = 'fromId'))

isochr_viz_frame <- mapping_data_prepper(vancouver_shape, isochr_frame,
                                         by = c('DBUID' = 'fromId'))

efficiency_frame <- read.csv('../../data/3_computed/transit_efficiency/efficiency_frame.csv')
efficiency_frame$fromId <- as.factor(efficiency_frame$fromId)
efficiency_frame_viz <- mapping_data_prepper(vancouver_shape, efficiency_frame,
                                             by = c('DBUID' = 'fromId'))

2) Interactive Visualization


# this cell used to be used for visualization experimentation
# the functions have since been migrated to the visualization section of:
# 0 - custom_functions

3) Map HTML Exports

Maps to export: 1) Score maps (32) 2) Isochrone maps (4) 3) Kepler maps (4) - performed elsewhere 4) Efficiency maps (2)

# 2 efficiency maps
map_maker_efficiency_cont(efficiency_frame_viz, 
                          bus_data = station_locs,
                          view = TRUE,
                          output_dir = paste0(mappath, '/efficiency_maps'))
[1] "Current Map: Continuous Efficiency Map"
[1] "Current Map: Continuous Efficiency Map with bus stops"

map_maker_efficiency_cont(efficiency_frame_viz, output_dir = paste0(mappath, '/efficiency_maps'))
LS0tDQp0aXRsZTogIlRyYXZlbCBUaW1lIE1hdHJpeCB0byBNYXBzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgTm90ZWJvb2sgUHVycG9zZQ0KDQpUaGlzIG5vdGVib29rIHNlcnZlcyB0byBzdW1tYXJpemUgdGhlIGVudGlyZSB2aXN1YWxpemF0aW9uIHByb2Nlc3MgZ29pbmcgZnJvbSANCnRoZSB0cmF2ZWwgdGltZSBtYXRyaXggdG8gdGhlIHZpc3VhbGl6YXRpb25zLiBUaGF0IGludm9sdmVzIHRoZSBmb2xsb3dpbmcgDQpzZWN0aW9uczoNCg0KMSkgSW1wb3J0aW5nIGFuZCBMaW5raW5nIEdlb3NwYXRpYWwgRGF0YQ0KMikgSW50ZXJhY3RpdmUgVmlzdWFsaXphdGlvbg0KMykgTWFwIEhUTUwgRXhwb3J0cw0KDQojIyAwKSBVc2VmdWwgTGlicmFyaWVzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRX0NCiMgaW1wb3J0IGN1c3RvbSBzY29yaW5nLCBjbGVhbmluZywgYW5kIHZpc3VhbGl6YXRpb24gZnVuY3Rpb25zDQpzb3VyY2UoJy4uLzAgLSBjdXN0b21fZnVuY3Rpb25zL2Z1bmN0aW9ucy5SJykNCg0KIyB3cmFuZ2xpbmcvY29udmVuaWVuY2UNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnbHVlKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShzZikNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoYml0NjQpDQoNCiMgdmlzdWFsaXphdGlvbg0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShtYXB2aWV3KTsgbWFwdmlld09wdGlvbnMocGxhdGZvcm0gPSAnbGVhZmdsJykNCg0KIyBGb3IgcHJldHR5IGtuaXR0aW5nDQpsaWJyYXJ5KGxlbW9uKQ0Ka25pdF9wcmludC5kYXRhLmZyYW1lIDwtIGxlbW9uX3ByaW50DQprbml0X3ByaW50LnRibCA8LSBsZW1vbl9wcmludA0Ka25pdF9wcmludC5zdW1tYXJ5IDwtIGxlbW9uX3ByaW50DQoNCnNoYXBlcGF0aCA8LSAiLi4vLi4vZGF0YS8xX3Jhdy9zaGFwZV9maWxlcyINCmNvbXBwYXRoIDwtICIuLi8uLi9kYXRhLzNfY29tcHV0ZWQvIg0KbWFwcGF0aCA8LSAiLi4vLi4vZGF0YS9odG1sX21hcHMiICAgICMgb3V0cHV0IGRpcmVjdG9yeQ0KDQpgYGANCg0KDQoNCg0KDQojIyAxKSBJbXBvcnRpbmcgRGF0YQ0KDQojIyMgSW1wb3J0IHRoZSBBY2Nlc3NpYmlsaXR5IE1lYXN1cmVzDQpgYGB7cn0NCnNjb3Jlc19mcmFtZSA8LSByZWFkLmNzdihwYXN0ZTAoY29tcHBhdGgsICcvYWNjZXNzaWJpbGl0eV9tZWFzdXJlcy9zY29yZXNfZnJhbWUuY3N2JykpDQppc29jaHJfZnJhbWUgPC0gcmVhZC5jc3YocGFzdGUwKGNvbXBwYXRoLCAnL2FjY2Vzc2liaWxpdHlfbWVhc3VyZXMvaXNvY2hyb25lX2ZyYW1lLmNzdicpKQ0KDQojIGNvbnZlcnQgZmFjdG9yIGNvbHVtbnMgdG8gZmFjdG9yDQpzY29yZXNfZnJhbWVbLCBjKDEsMiw0LDUpXSA8LSBsYXBwbHkoc2NvcmVzX2ZyYW1lWywgYygxLDIsNCw1KV0sIGFzLmZhY3RvcikNCmlzb2Nocl9mcmFtZVssIGMoMSwyLDMpXSA8LSBsYXBwbHkoaXNvY2hyX2ZyYW1lWywgYygxLDIsMyldLCBhcy5mYWN0b3IpDQoNCmhlYWQoc2NvcmVzX2ZyYW1lKQ0KaGVhZChpc29jaHJfZnJhbWUpDQoNCmBgYA0KDQojIyMgSW1wb3J0IHRoZSBkaXNzZW1pbmF0aW9uIGJsb2NrIHNoYXBlIGZpbGUNCg0KKk5vdGU6IFRoZSBzaGFwZSBmaWxlIGJlaW5nIGltcG9ydGVkIHdhcyBwcmVwcm9jY2Vzc2VkIGZyb20gYSBuYXRpb25hbCoNCipjZW5zdXMgc2hhcGUgZmlsZSBhbmQgZG9lcyBub3QgbmVlZCB0byBiZSBmaWx0ZXJlZC9jbGVhbmVkIGFueSBmdXJ0aGVyLioNCipHaXRCYXNoIHNjcmlwdGluZyAodmlhIGpxIGxpYnJhcnkpIHdhcyB1c2VkIHRvIGV4dHJhY3QgMzYgbWVnYWJ5dGVzIG9mKg0KKlZhbmNvdXZlciBwb2x5Z29uIGRhdGEgZnJvbSAxLjYgZ2lnYWJ5dGVzIG9mIENhbmFkYSB3aWRlIGdlb0pzb24gcG9seWdvbiBkYXRhLCoNCip3aGljaCBSIG5vciBQeXRob24gY291bGQgZWZmaWNpZW50bHkgaGFuZGxlKi4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KdmFuY291dmVyX3NoYXBlIDwtIHN0X3JlYWQocGFzdGUwKHNoYXBlcGF0aCwgJy9EQl9WYW5fQ01BLnNocCcpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgaWQgdG8gZmFjdG9yDQp2YW5jb3V2ZXJfc2hhcGUkREJVSUQgPC0gYXMuZmFjdG9yKHZhbmNvdXZlcl9zaGFwZSREQlVJRCkNCmhlYWQodmFuY291dmVyX3NoYXBlKQ0KDQpgYGANCg0KDQojIyMgSW1wb3J0IGJ1cyBzdG9wIG5ldHdvcmsgZGF0YQ0KYGBge1J9DQojIFJlYWQgVmFuY291dmVyIFN0YXRpb24gdHh0IGZpbGUNCnJhd2xvY3MgPC0gcmVhZC5jc3YoIi4uLy4uL2RhdGEvMV9yYXcvdHJhbnNpdF9hbmRfb3NtX2RhdGEvc3RvcHMudHh0IiwNCiAgICAgICAgICAgICAgICAgICAgaGVhZCA9IFRSVUUsIHNlcD0iLCIpDQoNCnN0YXRpb25fbG9jcyA8LSByYXdsb2NzW2MoInN0b3BfaWQiLCJzdG9wX25hbWUiLCJzdG9wX2xhdCIsInN0b3BfbG9uIildDQoNCmNvbG5hbWVzKHN0YXRpb25fbG9jcylbM10gPC0gImxhdGl0dWRlIg0KY29sbmFtZXMoc3RhdGlvbl9sb2NzKVs0XSA8LSAibG9uZ2l0dWRlIg0KDQojIENvbnZlcnQgdGhlIGNvbHVtbnMgaW1wb3J0ZWQgYXMgYSBmYWN0b3IgdG8gY2hhcmFjdGVycw0Kc3RhdGlvbl9sb2NzJHN0b3BfaWQgPC0gYXMuY2hhcmFjdGVyKHN0YXRpb25fbG9jcyRzdG9wX2lkKQ0Kc3RhdGlvbl9sb2NzJHN0b3BfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoc3RhdGlvbl9sb2NzJHN0b3BfbmFtZSkNCg0KIyBjaGVjayBmb3IgTkEgcm93cw0Kc3RhdGlvbl9sb2NzW2lzLm5hKGFzLm51bWVyaWMoc3RhdGlvbl9sb2NzJHN0b3BfaWQpKSwgXQ0KDQojIGNoZWNrIGhvdyBtYW55IHN0b3BzIHRoZXJlIGFyZQ0KdW5pcXVlTihzdGF0aW9uX2xvY3Mkc3RvcF9pZCkNCg0KaGVhZChzdGF0aW9uX2xvY3MpDQoNCmBgYA0KDQojIyMgSm9pbmluZyB0aGUgR2VvU3BhdGlhbCBEYXRhDQpgYGB7cn0NCg0KIyBtZXJnZXMgc2hhcGUgYW5kIHZpc3VhbGl6YXRpb24gZGF0YSwgdGhlbiB0cmFuc2Zvcm1zIHRoZW0gZm9yIHZpc3VhbGl6YXRpb25zDQojIGNycyA9IHRhcmdldCBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW0NCm1hcHBpbmdfZGF0YV9wcmVwcGVyIDwtIGZ1bmN0aW9uKHNoYXBlX2RhdGEsIHZpc3VhbGl6YXRpb25fZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gTlVMTCwgY3JzID0gNDMyNikgew0KICBpZiAoaXMubnVsbChieSkpIHsNCiAgICBtZXNzYWdlKCJNdXN0IHByb3ZpZGUga2V5IG9yIGNvbHVtbnMgdG8gam9pbiBvbi4gRm9yIGV4YW1wbGUsIG9uID0gYygnSUQnID0gJ2lkJykpIikNCiAgICByZXR1cm4oKQ0KICB9DQogIA0KICAjIGpvaW4gYm90aCBkYXRhc2V0cyBvbiB0aGUgc2hhcGVzIHNvIGFsbCBwb2x5Z29ucyBhcmUgaW5jbHVkZWQNCiAgc2hhcGVfdml6X2ZyYW1lIDwtIGxlZnRfam9pbihzaGFwZV9kYXRhLCB2aXN1YWxpemF0aW9uX2RhdGEsIGJ5ID0gYnkpDQogIA0KICAjIGNvbnZlcnQgdG8gc2ltcGxlIGZlYXR1cmUgb2JqZWN0IChzZikgdGhlbiB0cmFuc2Zvcm0gY29vcmRzDQogIHJldHVybihzdF90cmFuc2Zvcm0oc3RfYXNfc2Yoc2hhcGVfdml6X2ZyYW1lKSwgY3JzID0gY3JzKSkNCiAgICANCn0NCg0Kc2NvcmVzX3Zpel9mcmFtZSA8LSBtYXBwaW5nX2RhdGFfcHJlcHBlcih2YW5jb3V2ZXJfc2hhcGUsIHNjb3Jlc19mcmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCdEQlVJRCcgPSAnZnJvbUlkJykpDQoNCmlzb2Nocl92aXpfZnJhbWUgPC0gbWFwcGluZ19kYXRhX3ByZXBwZXIodmFuY291dmVyX3NoYXBlLCBpc29jaHJfZnJhbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygnREJVSUQnID0gJ2Zyb21JZCcpKQ0KDQplZmZpY2llbmN5X2ZyYW1lIDwtIHJlYWQuY3N2KCcuLi8uLi9kYXRhLzNfY29tcHV0ZWQvdHJhbnNpdF9lZmZpY2llbmN5L2VmZmljaWVuY3lfZnJhbWUuY3N2JykNCmVmZmljaWVuY3lfZnJhbWUkZnJvbUlkIDwtIGFzLmZhY3RvcihlZmZpY2llbmN5X2ZyYW1lJGZyb21JZCkNCmVmZmljaWVuY3lfZnJhbWVfdml6IDwtIG1hcHBpbmdfZGF0YV9wcmVwcGVyKHZhbmNvdXZlcl9zaGFwZSwgZWZmaWNpZW5jeV9mcmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygnREJVSUQnID0gJ2Zyb21JZCcpKQ0KDQpgYGANCg0KIyMgMikgSW50ZXJhY3RpdmUgVmlzdWFsaXphdGlvbg0KDQpgYGB7Un0NCg0KIyB0aGlzIGNlbGwgdXNlZCB0byBiZSB1c2VkIGZvciB2aXN1YWxpemF0aW9uIGV4cGVyaW1lbnRhdGlvbg0KIyB0aGUgZnVuY3Rpb25zIGhhdmUgc2luY2UgYmVlbiBtaWdyYXRlZCB0byB0aGUgdmlzdWFsaXphdGlvbiBzZWN0aW9uIG9mOg0KIyAwIC0gY3VzdG9tX2Z1bmN0aW9ucw0KDQoNCmBgYA0KDQoNCiMjIDMpIE1hcCBIVE1MIEV4cG9ydHMNCg0KKipNYXBzIHRvIGV4cG9ydDoqKg0KMSkgU2NvcmUgbWFwcyAoMzIpDQoyKSBJc29jaHJvbmUgbWFwcyAoNCkNCjMpIEtlcGxlciBtYXBzICg0KSAtICpwZXJmb3JtZWQgZWxzZXdoZXJlKg0KNCkgRWZmaWNpZW5jeSBtYXBzICgyKQ0KDQpgYGB7cn0NCg0KYW1lbml0aWVzIDwtIHVuaXF1ZShzY29yZXNfdml6X2ZyYW1lJHR5cGUpDQp3ZWlnaHRzIDwtIHVuaXF1ZShzY29yZXNfdml6X2ZyYW1lX3N0JHdlaWdodCkNCm5lYXJlc3RfbiA8LSB1bmlxdWUoc2NvcmVzX3Zpel9mcmFtZV9zdCRuZWFyZXN0X24pDQpidXNfbGV2ZWxzIDwtIGMoVFJVRSxGQUxTRSkNCg0KIyBDdXN0b20gZm9yIGxvb3AgZm9yIHNwZWNpZmljIG1hcCBvdXRwdXR0aW5nDQojIFJlbmRlcmluZyBkb3plbnMgb2YgbWFwcyBjYW4gdGFrZSBhIGxvbmcgdGltZSAoMTAtMjAgbWludXRlcykNCmZvciAoYW1lbml0eSBpbiBhbWVuaXRpZXMpIHsgDQogIA0KICBmb3IgKGFkZF9zdG9wIGluIGJ1c19sZXZlbHMpIHsNCiAgICANCiAgICAjIDQgaXNvY2hyb25lIG1hcHMNCiAgICBtYXBfbWFrZXJfaXNvY2hyb25lKGRhdGEgPSBpc29jaHJfdml6X2ZyYW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgYnVzX2RhdGEgPSBzdGF0aW9uX2xvY3MsDQogICAgICAgICAgICAgICAgICAgICAgICBhbWVuaXR5ID0gYW1lbml0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGJ1c19sZXZlbHMgPSBhZGRfc3RvcCwNCiAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dF9kaXIgPSBwYXN0ZTAobWFwcGF0aCwgJy9pc29jaHJvbmVfbWFwcycpKQ0KICANCiAgICBmb3IgKHd0IGluIHdlaWdodHMpIHsNCiAgICAgIGZvciAobiBpbiBuZWFyZXN0X24pIHsNCiAgICAgICAgDQogICAgICAgICMgMzIgc2NvcmUgbWFwcw0KICAgICAgICBtYXBfbWFrZXJfc2NvcmVzKGRhdGEgPSBzY29yZXNfdml6X2ZyYW1lLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJ1c19kYXRhID0gc3RhdGlvbl9sb2NzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFtZW5pdHkgPSBhbWVuaXR5LA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFkZF9zdG9wID0gYWRkX3N0b3AsDQogICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0PSB3dCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBuZWFyZXN0X24gPSBuLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dF9kaXIgPSBwYXN0ZTAobWFwcGF0aCwgJy9zY29yZV9tYXBzJykpDQogICAgICB9IA0KICAgIH0NCiAgfQ0KfQ0KDQpzb3VyY2UoJy4uLzAgLSBjdXN0b21fZnVuY3Rpb25zL2Z1bmN0aW9ucy5SJykNCg0KIyAyIGVmZmljaWVuY3kgbWFwcw0KbWFwX21ha2VyX2VmZmljaWVuY3lfY29udChlZmZpY2llbmN5X2ZyYW1lX3ZpeiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJ1c19kYXRhID0gc3RhdGlvbl9sb2NzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB2aWV3ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0X2RpciA9IHBhc3RlMChtYXBwYXRoLCAnL2VmZmljaWVuY3lfbWFwcycpKQ0KDQptYXBfbWFrZXJfZWZmaWNpZW5jeV9xdWFudChlZmZpY2llbmN5X2ZyYW1lX3ZpeiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1c19kYXRhID0gc3RhdGlvbl9sb2NzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmlldyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRwdXRfZGlyID0gcGFzdGUwKG1hcHBhdGgsICcvZWZmaWNpZW5jeV9tYXBzJykpDQoNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQptYXBfbWFrZXJfZWZmaWNpZW5jeV9jb250KGVmZmljaWVuY3lfZnJhbWVfdml6LCBvdXRwdXRfZGlyID0gcGFzdGUwKG1hcHBhdGgsICcvZWZmaWNpZW5jeV9tYXBzJykpDQoNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K